/*
 * Sylvain BERTRAND <digital.ragnarok@gmail.com>
 * code protected by GNU GPL v3
 */
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <glib.h>
#include <libxml/xinclude.h>
#include "../../toolkit/xml.h"
#include "../../toolkit/keyboard.h"
#include "../misc.h"
#include "../geom.h"
#include "../frame.h"
#include "../actions.h"
#include "../focus_cycle.h"
#include "../window.h"
#include "../stacking.h"
struct options {
	gboolean linear;
	gboolean desktop_windows;
	gboolean only_hilite_windows;
	gboolean all_desktops;
	gboolean forward;
	gboolean bar;
	gboolean raise;
	GSList *actions;
	gboolean cancel;
	guint state;
};

static gboolean i_input_func(guint initial_state, XEvent *e, struct t_ic *ic, gpointer options, gboolean *used)
{
	struct options *o = options;
	guint mods;
	(void)ic;
	(void)used;
	mods = t_keyboard_only_modmasks(e->xkey.state);
	if(e->type == KeyRelease) {
		mods &= ~t_keyboard_keyevent_to_modmask(e);
	}
	if(e->type == KeyPress) {
		KeySym sym = t_keyboard_keypress_to_keysym(e);
		if(sym == XK_Escape) {
			o->cancel = TRUE;
			o->state = e->xkey.state;
			return FALSE;
		} else if((sym == XK_Return || sym == XK_KP_Enter) && !initial_state) {
			o->cancel = FALSE;
			o->state = e->xkey.state;
			return FALSE;
		}
	} else if(e->type == KeyRelease && initial_state && !(mods & initial_state)) {
		o->cancel = FALSE;
		o->state = e->xkey.state;
		return FALSE;
	}
	return TRUE;
}

static void i_cancel_func(gpointer options)
{
	struct options *o = options;
	o->cancel = TRUE;
	o->state = 0;
}

static void i_post_func(gpointer options)
{
	struct options *o = options;
	struct wm_client *ft;
	ft = focus_cycle(o->forward, o->all_desktops, !o->only_hilite_windows, o->desktop_windows, o->linear, TRUE, o->bar, TRUE,
						o->cancel);
	if(ft)
		actions_run_acts(o->actions, WM_USER_ACTION_KEYBOARD_KEY, o->state, -1, -1, 0, WM_FRAME_CONTEXT_NONE, ft);
	stacking_restore();
}


static gpointer setup_func(xmlNodePtr node, wm_actions_i_pre_func *pre, wm_actions_i_input_func *input,
	wm_actions_i_cancel_func *cancel, wm_actions_i_post_func *post)
{
	xmlNodePtr n;
	struct options *o;
	(void)pre;
	o = g_slice_new0(struct options);
	o->bar = TRUE;
	if((n = t_xml_find_node(node, "linear")))
		o->linear = t_xml_node_bool(n);
	if((n = t_xml_find_node(node, "bar")))
		o->bar = t_xml_node_bool(n);
	if((n = t_xml_find_node(node, "raise")))
		o->raise = t_xml_node_bool(n);
	if((n = t_xml_find_node(node, "hilite")))
		o->only_hilite_windows = t_xml_node_bool(n);
	if((n = t_xml_find_node(node, "desktop")))
		o->desktop_windows = t_xml_node_bool(n);
	if((n = t_xml_find_node(node, "allDesktops")))
		o->all_desktops = t_xml_node_bool(n);
	if((n = t_xml_find_node(node, "finalactions"))) {
		xmlNodePtr m;
		m = t_xml_find_node(n->children, "action");
		while(m) {
			struct wm_actions_act *action = actions_parse(m);
			if(action)
				o->actions = g_slist_append(o->actions, action);
			m = t_xml_find_node(m->next, "action");
		}
	} else {
		o->actions = g_slist_prepend(o->actions, actions_parse_string("Focus"));
		o->actions = g_slist_prepend(o->actions, actions_parse_string("Raise"));
	}
	*input = i_input_func;
	*cancel = i_cancel_func;
	*post = i_post_func;
	return o;
}

static gpointer setup_forward_func(xmlNodePtr node, wm_actions_i_pre_func *pre, wm_actions_i_input_func *input,
	wm_actions_i_cancel_func *cancel, wm_actions_i_post_func *post)
{
	struct options *o = setup_func(node, pre, input, cancel, post);
	o->forward = TRUE;
	return o;
}

static gpointer setup_backward_func(xmlNodePtr node, wm_actions_i_pre_func *pre, wm_actions_i_input_func *input,
	wm_actions_i_cancel_func *cancel, wm_actions_i_post_func *post)
{
	struct options *o = setup_func(node, pre, input, cancel, post);
	o->forward = FALSE;
	return o;
}

static void free_func(gpointer options)
{
	struct options *o = options;
	while(o->actions) {
		actions_act_unref(o->actions->data);
		o->actions = g_slist_delete_link(o->actions, o->actions);
	}
	g_slice_free(struct options, o);
}

static gboolean run_func(struct wm_actions_data *data, gpointer options)
{
	struct options *o = options;
	struct wm_client *ft;
	(void)data;
	ft = focus_cycle(o->forward, o->all_desktops, !o->only_hilite_windows, o->desktop_windows, o->linear, TRUE, o->bar, FALSE,
						FALSE);
	stacking_restore();
	if(o->raise && ft)
		stacking_temp_raise(CLIENT_AS_WINDOW(ft));
	return TRUE;
}

void action_cyclewindows_startup(void)
{
	actions_register_i("NextWindow", setup_forward_func, free_func, run_func);
	actions_register_i("PreviousWindow", setup_backward_func, free_func, run_func);
}
